package gao.xiaolei.service;

import javax.annotation.Resource;

import gao.xiaolei.util.UuidUtil;
import org.springframework.transaction.annotation.Transactional;
import gao.xiaolei.dao.*;
import gao.xiaolei.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import gao.xiaolei.entity.GradeRule;
import gao.xiaolei.entity.TestPersonalGrade;
import gao.xiaolei.entity.TestProject;
import xiaolei.gao.git.IGit;
import gao.xiaolei.util.ServiceName;
import xiaolei.gao.git.MyGitExcepition;

import java.time.LocalDateTime;
import java.util.*;

@Service("TestProjectService")
public class TestProjectService {

    @Resource(name = "TestProjectDao")
    private TestProjectDao testProjectDao;

    @Resource(name = "GradeRuleDao")
    private GradeRuleDao gradeRuleDao;

    @Resource(name = "TestProcessDao")
    private TestProcessDao testProcessDao;

    @Resource(name = "TestSystemDao")
    private TestSystemDao testSystemDao;

    @Autowired
    private RestTemplate restTemplate;

    @Resource(name = "GitInterface")
    private IGit gitInterface;

    @Resource(name = "TestPersonalGradeDao")
    private TestPersonalGradeDao testPersonalGradeDao;

    @Resource(name = "TestTeamGradeDao")
    private TestTeamGradeDao testTeamGradeDao;

    @Resource(name = "TestRequirementDao")
    private TestRequirementDao testRequirementDao;

    @Resource(name = "TestRequirementCheckDao")
    private TestRequirementCheckDao testRequirementCheckDao;

    @Resource(name = "TestDesignDao")
    private TestDesignDao testDesignDao;

    @Resource(name = "TestStepDao")
    private TestStepDao testStepDao;

    @Resource(name = "TestRequirementDesignCoverDao")
    private TestRequirementDesignCoverDao testRequirementDesignCoverDao;

    @Resource(name = "TestDesignCheckDao")
    private TestDesignCheckDao testDesignCheckDao;

    @Resource(name = "TestRunDao")
    private TestRunDao testRunDao;

    @Resource(name = "TestRunLogDao")
    private TestRunLogDao testRunLogDao;

    @Resource(name = "TestRunLogStepDao")
    private TestRunLogStepDao testRunLogStepDao;

    @Resource(name = "TestRunCheckDao")
    private TestRunCheckDao testRunCheckDao;

    @Resource(name = "TestCopyService")
    private TestCopyService testCopyService;

    @Resource(name = "CreateNoticeTaskService")
    private CreateNoticeTaskService createNoticeTaskService;


    @Transactional
    //添加测试项目
    public void addTestProject(TestProject testProject) throws MyGitExcepition {
        testProject.setCreateDate(LocalDateTime.now());
        testProjectDao.save(testProject);
        gitInterface.creatLocalGit(testProject.getId());
    }

    //根据组Id获取项目的数量
    public Integer getCountByGroupId(String groupId) {
        return testProjectDao.countByGroupId(groupId);
    }

    @Transactional(readOnly = true)
    //根据组Id获取项目列表（树形列表）
    public List<TestTreeDto> findProjectListByGroupId(String groupId) {
        List<Object[]> projectList = testProjectDao.queryIdAndNameByGroupId(groupId);
        return objToTreeDto(projectList, groupId);
    }

    //根据组Id获取项目列表（供生成access_token使用的）
    public List<String> findProjectListByGroupIdForAuth(String groupId) {
        return testProjectDao.getIdsByGroupId(groupId);
    }

    @Transactional
    //修改测试项目
    public void updateTestProject(TestProject testProject) {
        TestProject oldTestProject = testProjectDao.findById(testProject.getId()).orElseThrow(() -> new IllegalArgumentException(
                "该测试项目".concat("(")
                        .concat(testProject.getId())
                        .concat(")不存在！")
        ));
        oldTestProject.setName(testProject.getName());
        oldTestProject.setDescribe(testProject.getDescribe());
        oldTestProject.setSystem(testProject.getSystem());
        oldTestProject.setGroupId(testProject.getGroupId());
        testProjectDao.save(oldTestProject);
    }

    @Transactional
    //删除组下面的测试项目
    public void deleteTestProjectByGroup(String groupId) {
        List<String> projectIds = testProjectDao.getIdsByGroupId(groupId);
        for (int i = 0, length = projectIds.size(); i < length; i++) {
            deleteTestProject(projectIds.get(i));
        }
    }

    @Transactional
    //删除测试项目列表
    public void deleteTestProjectList(List<String> projectIds) {
        int i, length;
        for (i = 0, length = projectIds.size(); i < length; i++) {
            String id = projectIds.get(i);
            if (!testProjectDao.existsById(id))
                throw new IllegalArgumentException(
                        "该测试项目".concat("(")
                                .concat(id)
                                .concat(")不存在！"));
            else {
                testProjectDao.deleteById(id);//将项目本身删除掉
                gradeRuleDao.deleteAllByToProject(id);//将这个项目的评分规则删除掉
                testProcessDao.deleteAllByToProject(id);//将这个项目的计划流程删除掉
                deleteItemByProject(id);//将项目下的需求之类的内容删除掉
                testCopyService.deleteAllCopyByProject(id);//将项目下的副本删除掉
                testPersonalGradeDao.deleteAllByProject(id);//将项目下的个人成绩删除掉
                testTeamGradeDao.deleteAllByProject(id);//将项目下的团队成绩删除掉
            }
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        for (i = 0, length = projectIds.size(); i < length; i++) {//最后才去取消定时任务
            HttpEntity<JobkeyDto> httpEntity = new HttpEntity<>(new JobkeyDto(null, projectIds.get(i), 1), headers);
            restTemplate.postForEntity(ServiceName.BUS_SERVICE.getServiceName().concat("/message/quertz"), httpEntity, String.class);
        }
    }

    @Transactional
    //删除测试项目
    public void deleteTestProject(String id) {
        if (!testProjectDao.existsById(id))
            throw new IllegalArgumentException(
                    "该测试项目".concat("(")
                            .concat(id)
                            .concat(")不存在！"));
        else {
            testProjectDao.deleteById(id);//将项目本身删除掉
            gradeRuleDao.deleteAllByToProject(id);//将这个项目的评分规则删除掉
            testProcessDao.deleteAllByToProject(id);//将这个项目的计划流程删除掉
            deleteItemByProject(id);//将项目下的需求之类的内容删除掉
            testCopyService.deleteAllCopyByProject(id);//将项目下的副本删除掉
            testPersonalGradeDao.deleteAllByProject(id);//将项目下的个人成绩删除掉
            testTeamGradeDao.deleteAllByProject(id);//将项目下的团队成绩删除掉
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<JobkeyDto> httpEntity = new HttpEntity<>(new JobkeyDto(null, id, 1), headers);
            restTemplate.postForEntity(ServiceName.BUS_SERVICE.getServiceName().concat("/message/quertz"), httpEntity, String.class);//将这个项目的计划流程的定时任务取消掉
        }
    }

    @Transactional(readOnly = true)
    //获取所有测试项目
    public List<TestProjectOutDto> findAllTestProject() {
        List<TestProject> projects = testProjectDao.findAll();
        return getTestProjectOutDtos(projects);
    }

    @Transactional(readOnly = true)
    //根据负责人来获取所有测试项目
    public List<TestProjectOutDto> findAllTestProjectByLeadingCadre(String leadingCadre) {
        List<TestProject> projects = testProjectDao.findAllByLeadingCadre(leadingCadre);
        return getTestProjectOutDtos(projects);
    }

    @Transactional(readOnly = true)
    //获取测试项目的信息
    public TestProjectOutDto getDetail(String id) {
        TestProject testProject = testProjectDao.findById(id).orElseThrow(() -> new IllegalArgumentException(
                "该测试项目".concat("(")
                        .concat(id)
                        .concat(")不存在！")
        ));
        String leadingCadreName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName()
                        .concat("/user/realName/")
                        .concat(testProject.getLeadingCadre())
                , String.class);
        String groupName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName()
                        .concat("/user/groupName/")
                        .concat(testProject.getGroupId())
                , String.class);
        String systemName = testSystemDao.getSystemName(testProject.getSystem()).orElse("不详");
        return new TestProjectOutDto(testProject, leadingCadreName, groupName, systemName, null);
    }

    //根据组的负责人获取组的信息
    public List<GroupDto> getGroupByLeadingCadre(String leadingCadre) {
        return restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/group/leadingCadre/").concat(leadingCadre), List.class);
    }

    //老师或者主任获取用户在某个班级下每个组的每个项目的成绩信息
    @Transactional(readOnly = true)
    public List<GradeOutDto> getClassesMemberGradeInWitchProject(String classesId, String userId) {
        List<String> classesGroups = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/classes/user/inWitchGroup/").concat(classesId).concat("/").concat(userId), List.class);
        List<GradeOutDto> gradeOutDtos = new LinkedList<>();
        for (int i = 0, length = classesGroups.size(); i < length; i++) {
            gradeOutDtos.addAll(this.getGroupMemberGradeInWitchProject(classesGroups.get(i), userId));
        }
        return gradeOutDtos;
    }

    //老师通知组员项目的成绩出来了
    @Transactional
    public void noticeUserInProjectGradePublic(String projectId, String userId) {
        TestProject project = testProjectDao.findById(projectId).orElseThrow(() -> new IllegalArgumentException("该项目("
                .concat(projectId)
                .concat(")不存在！")));
        testPersonalGradeDao.updateIsPublic(true, projectId);//将这个项目的所有成绩设置为对本人可见
        String groupName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/groupName/").concat(project.getGroupId()), String.class);
        String classesName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/classes/name/group/").concat(project.getGroupId()), String.class);
        List<String> groupMember = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/groupMember/").concat(project.getGroupId()), List.class);
        Task task = Task.builder()
                .id(UuidUtil.get32Uuid())
                .name("成绩出炉")
                .responsible(userId)//系统通知的发件人都是管理员
                .isAttachment(0)
                .isGroup(0)
                .status(1)
                .type(4)
                .priority(1)
                .description("你所在的班级["
                        .concat(classesName)
                        .concat("]的组[")
                        .concat(groupName)
                        .concat("]的项目[")
                        .concat(project.getName())
                        .concat("]的成绩出炉了"))
                .build();
        createNoticeTaskService.createNoticeTask(task, groupMember);
    }

    //老师或者主任获取用户在某个组下每个项目的成绩信息
    @Transactional(readOnly = true)
    public List<GradeOutDto> getGroupMemberGradeInWitchProject(String groupId, String userId) {
        List<Object[]> objects = testProjectDao.queryIdAndNameByGroupId(groupId);//找到该组下的所有项目
        List<GradeOutDto> gradeOutDtos = new LinkedList<>();
        for (int i = 0, length = objects.size(); i < length; i++) {
            gradeOutDtos.addAll(getUserGradeInWitchProject(userId, (String) objects.get(i)[0], (String) objects.get(i)[1], groupId, false));
        }
        return gradeOutDtos;
    }

    //用户获取用户某个项目的成绩信息
    @Transactional(readOnly = true)
    public List<GradeOutDto> getUserGradeInWitchProjectForUser(String userId, String project) {
        return getUserGradeInWitchProject(userId, project, null, testProjectDao.getGroupId(project).get(), true);
    }

    //isSingle表示是用于该用户所有项目的查询还是查询单个项目的成绩
    private List<GradeOutDto> getUserGradeInWitchProject(String userId, String project, String projectName, String groupId, boolean isSingle) {
        GradeRule gradeRule = gradeRuleDao.findByToProject(project);//找到该项目的评分规则
        TestPersonalGrade testPersonalGrade = isSingle ? testPersonalGradeDao.findByUserIdAndProjectAndIsPublic(userId, project, true) : testPersonalGradeDao.findByUserIdAndProject(userId, project);//找到这个人在该项目下的成绩
        List<GradeOutDto> gradeOutDtos = new LinkedList<>();
        String groupField = isSingle ? null : project.concat(projectName);//groupFile只有在查询该用户所有项目的成绩时用于分组
        if (testPersonalGrade != null) {//如果找不到成绩说明此成绩没有结算或者此人现在还不能看成绩
            if (gradeRule.getTrqIsInclude() == 1)//如果评分规则包括测试需求的话
                gradeOutDtos.add(new GradeOutDto("测试需求", testPersonalGrade.getTrgGrade(), gradeRule.getTrqPercent(), groupField));
            if (gradeRule.getDesignIsInclude() == 1)//如果评分规则包括测试计划的话
                gradeOutDtos.add(new GradeOutDto("测试计划", testPersonalGrade.getDesignGrade(), gradeRule.getDesignPercent(), groupField));
            if (gradeRule.getRunIsInclude() == 1)//如果评分规则包括测试执行的话
                gradeOutDtos.add(new GradeOutDto("测试执行", testPersonalGrade.getRunGrade(), gradeRule.getRunPercent(), groupField));
            if (gradeRule.isBugIsInclude())//如果评分规则包括测试Bug的话
                gradeOutDtos.add(new GradeOutDto("测试BUG", testPersonalGrade.getBugGrade(), gradeRule.getBugPercent(), groupField));
            //            if (gradeRule.getReportIsInclude() == 1)//如果评分规则包括测试报告的话
//                gradeOutDtos.add(new GradeOutDto("测试报告", testPersonalGrade.getReportGrade(), gradeRule.getReportPercent(), groupField));
            if (gradeRule.getIsCheckOnWork() == 1)//如果评分规则包括考勤的话
                gradeOutDtos.add(new GradeOutDto("考勤", testPersonalGrade.getCheckGrade(), gradeRule.getCheckPercent(), groupField));
            if (gradeRule.getIsHasOtherGrade() == 1)//如果评分规则包括其他自定义评分标准的话
                gradeOutDtos.add(new GradeOutDto(gradeRule.getOtherGradeName(), testPersonalGrade.getOtherGrade(), gradeRule.getOtherGradePercent(), groupField));
            gradeOutDtos.add(new GradeOutDto("个人分数", testPersonalGrade.getPersonalGrade(), 100, groupField));
            if (gradeRule.getIsComposeWithTeamGrade() == 1)//如果评分规则包括团队分数的话
                gradeOutDtos.add(new GradeOutDto("团队分数", testTeamGradeDao.getGradeByProjectAndTeamId(project, groupId), gradeRule.getTeamGradePercent(), groupField));
            gradeOutDtos.add(new GradeOutDto("总分", testPersonalGrade.getTotal(), 100, groupField));
        }
        return gradeOutDtos;
    }

    @Transactional(readOnly = true)
    //查找到该用户所属的小组以供用户选择某个项目进入
    public List<TestProjectOutDto> getUserWitchProjectCanInto(String userId) {
        //因为得到的是LinkedHashMap,不能直接转成对象
        List<LinkedHashMap<String, String>> groups = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/group/member/").concat(userId), List.class);
        List<TestProjectOutDto> outs = new LinkedList<>();
        int i, j, length, length2;
        String testSystemName = null, leadingCadreName = null;
        for (i = 0, length = groups.size(); i < length; i++) {
            List<TestProject> groupProject = testProjectDao.findAllByGroupId(groups.get(i).get("id"));
            for (j = 0, length2 = groupProject.size(); j < length2; j++) {
                testSystemName = testSystemDao.getSystemName(groupProject.get(j).getSystem()).orElse("不详");//获取项目对应系统名称
                leadingCadreName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/realName/").concat(groupProject.get(j).getLeadingCadre()), String.class);//获取项目负责人昵称
                outs.add(new TestProjectOutDto(groupProject.get(j), leadingCadreName, groups.get(i).get("name"), testSystemName, groups.get(i).get("leaderName")));
            }
        }
        return outs;
    }

    //将项目下的测试需求、计划、执行都删除掉(只是删除数据库)
    private void deleteItemByProject(String projectId) {
        List<String> trqId = testRequirementDao.findIdsByProject(projectId);//获取测试需求Id
        testRequirementCheckDao.deleteAllByTargetIdIn(trqId);//删除测试需求的审核记录
        List<String> designId = testDesignDao.findIdsByProject(projectId);//获取测试计划Id
        testStepDao.deleteAllByTestDesignIn(designId);//删除测试计划下的步骤
        testRequirementDesignCoverDao.deleteAllByTestDesignIdIn(designId);//删除测试计划下的关联测试需求
        testDesignCheckDao.deleteAllByTargetIdIn(designId);//删除测试计划的审核记录
        List<String> runId = testRunDao.findIdsByProject(projectId);//获取测试执行Id
        if (runId.size() > 0) {//把空集合放到in()会抛异常
            List<String> runLogIds = testRunLogDao.findIdsByTestRunIdIn(runId);//获取测试执行记录Id
            testRunLogStepDao.deleteAllByTestRunLogIn(runLogIds);//删除测试执行记录下的测试步骤
            testRunLogDao.deleteAllByTestRunIdIn(runId);//删除测试执行记录
            testRunCheckDao.deleteAllByTargetIdIn(runId);//删除测试执行的审核记录
        }
        testRequirementDao.deleteAllByProject(projectId);//删除测试需求
        testDesignDao.deleteAllByProject(projectId);//删除测试计划
        testRunDao.deleteAllByProject(projectId);//删除测试执行
    }


    private List<TestProjectOutDto> getTestProjectOutDtos(List<TestProject> projects) {
        List<TestProjectOutDto> outDtos = new ArrayList<>(projects.size());
        String systemName = null, groupName = null;
        for (int i = 0, length = projects.size(); i < length; i++) {//添加测试系统的名称
            systemName = projects.get(i).getSystem().concat(testSystemDao.getSystemName(projects.get(i).getSystem()).orElse("不详"));
            groupName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName()
                            .concat("/user/groupName/")
                            .concat(projects.get(i).getGroupId())
                    , String.class);
            TestProjectOutDto out = new TestProjectOutDto(projects.get(i), null, groupName, systemName, null);
            outDtos.add(out);
        }
        return outDtos;
    }

    private List<TestTreeDto> objToTreeDto(List<Object[]> projectList, String parentId) {
        List<TestTreeDto> dtos = new ArrayList<>(projectList.size());
        for (int i = 0, length = projectList.size(); i < length; i++) {
            dtos.add(new TestTreeDto((String) projectList.get(i)[0], (String) projectList.get(i)[1], parentId, 0, null, 4, null));
        }
        return dtos;
    }
}
